/**
 * This file is part of aion-emu <aion-emu.com>.
 *
 *  aion-emu is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  aion-emu is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with aion-emu.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.aionemu.gameserver.model.account;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.aionemu.commons.database.dao.DAOManager;
import com.aionemu.gameserver.configs.main.CacheConfig;
import com.aionemu.gameserver.dao.InventoryDAO;
import com.aionemu.gameserver.dao.PlayerAppearanceDAO;
import com.aionemu.gameserver.dao.PlayerDAO;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.player.PlayerAppearance;
import com.aionemu.gameserver.model.gameobjects.player.PlayerCommonData;
import com.aionemu.gameserver.model.gameobjects.player.Storage;
import com.aionemu.gameserver.model.gameobjects.player.StorageType;
import com.aionemu.gameserver.services.PlayerService;
import com.aionemu.gameserver.utils.collections.cachemap.CacheMap;
import com.aionemu.gameserver.utils.collections.cachemap.CacheMapFactory;

/**
 * This class represents game account and is holding such informations as:
 * <ul>
 * <li>account id</li>
 * <li>account name</li>
 * <li> {@link AccountTime account time info}</li>
 * <li>a list of {@link PlayerAccountData} objects each of which keeping information about player that must be available
 * on character selection screen.</li>
 * </ul>
 * 
 * @author SoulKeeper
 */
public class Account implements Iterable<PlayerAccountData>
{
	private static final Logger					log			= Logger.getLogger(Account.class);
	private static CacheMap<Integer, Account>	accountsMap	= CacheMapFactory.createSoftCacheMap("Account", "account");

	/**
	 * Returns {@link Account} object that has given id.
	 * 
	 * @param accountId
	 * @param accountTime
	 * @param accountName
	 * @param accessLevel
	 * @param membership
	 * @return Account
	 */
	public static Account getAccount(int accountId, String accountName, AccountTime accountTime, byte accessLevel, byte membership)
	{
		log.debug("[AS] request for account: " + accountId);

		Account account = accountsMap.get(accountId);
		if (account == null)
		{
			account = loadAccount(accountId);

			if (CacheConfig.CACHE_ACCOUNTS)
				accountsMap.put(accountId, account);
		}

		account.setName(accountName);
		account.setAccountTime(accountTime);
		account.setAccessLevel(accessLevel);
		account.setMembership(membership);

		removeDeletedCharacters(account);

		return account;
	}

	/**
	 * Removes from db characters that should be deleted (their deletion time has passed).
	 * 
	 * @param account
	 */
	private static void removeDeletedCharacters(Account account)
	{
		/* Removes chars that should be removed */
		Iterator<PlayerAccountData> it = account.iterator();
		while (it.hasNext())
		{
			PlayerAccountData pad = it.next();
			int deletionTime = pad.getDeletionTimeInSeconds() * 1000;
			if (deletionTime != 0 && deletionTime <= System.currentTimeMillis())
			{
				it.remove();
				PlayerService.deletePlayerFromDB(pad.getPlayerCommonData().getObjectId());
			}
		}
	}

	/**
	 * Loads account data and returns.
	 * 
	 * @param accountId
	 * @param accountName
	 * @return
	 */
	private static Account loadAccount(int accountId)
	{
		Account account = new Account(accountId);

		PlayerDAO playerDAO = DAOManager.getDAO(PlayerDAO.class);
		PlayerAppearanceDAO appereanceDAO = DAOManager.getDAO(PlayerAppearanceDAO.class);

		List<Integer> playerOids = playerDAO.getPlayerOidsOnAccount(accountId);

		for (int playerOid : playerOids)
		{
			PlayerCommonData playerCommonData = playerDAO.loadPlayerCommonData(playerOid);
			PlayerAppearance appereance = appereanceDAO.load(playerOid);

			/**
			 * Load only equipment and its stones to display on character selection screen
			 */
			List<Item> equipment = DAOManager.getDAO(InventoryDAO.class).loadEquipment(playerOid);

			PlayerAccountData acData = new PlayerAccountData(playerCommonData, appereance, equipment);
			playerDAO.setCreationDeletionTime(acData);

			account.addPlayerAccountData(acData);
		}

		/**
		 * load account warehouse only once
		 */
		Storage accWarehouse = DAOManager.getDAO(InventoryDAO.class).loadStorage(null, account.getId(), StorageType.ACCOUNT_WAREHOUSE);
		Item.loadItemStones(accWarehouse.getStorageItems());
		account.setAccountWarehouse(accWarehouse);

		/**
		 * For new accounts - create empty account warehouse
		 */
		if (account.getAccountWarehouse() == null)
		{
			account.setAccountWarehouse(new Storage(StorageType.ACCOUNT_WAREHOUSE));
			account.getAccountWarehouse().setOwnerId(account.getId());
		}

		return account;
	}

	/** Unique id of this account (it's generated by login server) */
	private final int								id;
	/** Unique name of this account */
	private String									name;
	/**
	 * Access level
	 */
	private byte									accessLevel;
	/**
	 * Membership of this account
	 */
	private byte									membership;

	private AccountTime								accountTime;

	private final Map<Integer, PlayerAccountData>	players	= new HashMap<Integer, PlayerAccountData>();

	private Storage									accountWarehouse;

	public Account(int id)
	{
		this.id = id;
	}

	public int getId()
	{
		return id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public AccountTime getAccountTime()
	{
		return accountTime;
	}

	public void setAccountTime(AccountTime accountTime)
	{
		this.accountTime = accountTime;
	}

	/**
	 * @return the accessLevel
	 */
	public byte getAccessLevel()
	{
		return accessLevel;
	}

	/**
	 * @param accessLevel the accessLevel to set
	 */
	public void setAccessLevel(byte accessLevel)
	{
		this.accessLevel = accessLevel;
	}

	/**
	 * @return the membership
	 */
	public byte getMembership()
	{
		return membership;
	}

	/**
	 * @param membership the membership to set
	 */
	public void setMembership(byte membership)
	{
		this.membership = membership;
	}

	@Override
	public boolean equals(Object o)
	{
		if (this == o)
		{
			return true;
		}

		if (!(o instanceof Account))
		{
			return false;
		}

		Account account = (Account) o;

		return id == account.id;
	}

	@Override
	public int hashCode()
	{
		return id;
	}

	/**
	 * @param chaOid
	 * @return PlayerAccountData
	 */
	public PlayerAccountData getPlayerAccountData(int chaOid)
	{
		return players.get(chaOid);
	}

	/**
	 * @param accPlData
	 */
	public void addPlayerAccountData(PlayerAccountData accPlData)
	{
		players.put(accPlData.getPlayerCommonData().getObjectId(), accPlData);
	}

	/**
	 * @return the accountWarehouse
	 */
	public Storage getAccountWarehouse()
	{
		return accountWarehouse;
	}

	/**
	 * @param accountWarehouse the accountWarehouse to set
	 */
	public void setAccountWarehouse(Storage accountWarehouse)
	{
		this.accountWarehouse = accountWarehouse;
	}

	/** Returns the number of players that are on this account */
	public int size()
	{
		return players.size();
	}

	/**
	 * Sorts the accounts on last online.
	 */
	public ArrayList<PlayerAccountData> getSortedAccountsList()
	{
		ArrayList<PlayerAccountData> list = new ArrayList<PlayerAccountData>();
		list.addAll(players.values());
		Collections.sort(list, new Comparator<PlayerAccountData>()
		{
			@Override
			public int compare(PlayerAccountData x, PlayerAccountData y)
			{
				Integer t1 = x.getPlayerCommonData().getLastOnline();
				Integer t2 = y.getPlayerCommonData().getLastOnline();
				if (t2 == 0)
					return 1;
				else if (t1 == 0)
					return -1;
				return -t1.compareTo(t2);
			}
		});
		return list;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Iterator<PlayerAccountData> iterator()
	{
		return players.values().iterator();
	}
}
